// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CommandInvoker.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// <summary>
//   Defines the CommandInvoker type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon.Windows.Input.Commands
{
    using System;
    using System.Windows;
    using System.Windows.Input;
    using Photon;

    // ReSharper disable StaticFieldInGenericType

    public abstract class CommandInvoker<TTarget, TDispatcher> 
        where TTarget : UIElement
        where TDispatcher: CommandDispatcher<TTarget>, new()
    {
        private static readonly DependencyProperty CommandDispatcherProperty = DependencyProperty.RegisterAttached(
            "CommandDispatcher", typeof(TDispatcher), typeof(CommandInvoker<TTarget, TDispatcher>), null);
        
        public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
            "Command", typeof(ICommand), typeof(CommandInvoker<TTarget, TDispatcher>), new PropertyMetadata(CommandChanged));

        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached(
            "CommandParameter", typeof(object), typeof(CommandInvoker<TTarget, TDispatcher>), new PropertyMetadata(CommandParameterChanged));
        
        public static void SetCommand(TTarget target, ICommand command)
        {
            target.SetValue(CommandProperty, command);
        }

        public static ICommand GetCommand(TTarget target)
        {
            return target.GetValue(CommandProperty) as ICommand;
        }

        public static void SetCommandParameter(TTarget target, object parameter)
        {
            target.SetValue(CommandParameterProperty, parameter);
        }

        public static object GetCommandParameter(TTarget target)
        {
            return target.GetValue(CommandParameterProperty);
        }

        private static void CommandChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var target = dependencyObject as TTarget;
            if (target != null)
            {
                var dispatcher = GetOrCreateDispatcher(target);
                dispatcher.Command = e.NewValue as ICommand;
            }
        }

        private static void CommandParameterChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var target = dependencyObject as TTarget;
            if (target != null)
            {
                var dispatcher = GetOrCreateDispatcher(target);
                dispatcher.CommandParameter = e.NewValue;
            }
        }

        private static TDispatcher GetOrCreateDispatcher(TTarget target)
        {
            var dispatcher = target.GetValue(CommandDispatcherProperty) as TDispatcher;
            if (dispatcher == null)
            {
                dispatcher = new TDispatcher
                    {
                        Target = target
                    };
                dispatcher.BeforeInvoke += OnBeforeInvoke;
                
                // set dispatcher
                target.SetValue(CommandDispatcherProperty, dispatcher);
            }
            return dispatcher;
        }

        private static void OnBeforeInvoke(object sender, EventArgs e)
        {
            // get dispatcher
            var dispatcher = (TDispatcher)sender;
            
            // re-apply any binding to ensure we are using the latest value (common issue with Silverlight)
            var element = dispatcher.Target as FrameworkElement;
            if (element != null)
            {
                var expression = element.GetBindingExpression(CommandParameterProperty);
                if (expression != null)
                {
                    element.SetBinding(CommandParameterProperty, expression.ParentBinding);
                }
            }
        }

        public class AutoEventCommandDispatcher : EventCommandDispatcher<TTarget> 
        {
            internal AutoEventCommandDispatcher()
            {
                
            }

            protected override IEventBinder EventBinder
            {
                get { return Photon.EventBinder.Get<TTarget>(GetType().DeclaringType.Name); }
            }
        }
    }

    // ReSharper restore StaticFieldInGenericType


}